package net.gdface.facelog;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
import static com.google.common.base.Preconditions.*;
import static gu.sql2java.Managers.*;
import static net.gdface.facelog.DaoManagement.*;

import com.google.common.base.Functions;
import com.google.common.base.MoreObjects;
import com.google.common.base.Predicates;
import com.google.common.base.Strings;
import com.google.common.base.Throwables;
import com.google.common.collect.Collections2;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import gu.simplemq.json.BaseJsonEncoder;
import gu.sql2java.RowMetaData;
import gu.sql2java.StringMatchType;
import gu.sql2java.exception.RuntimeDaoException;
import net.gdface.facelog.db.DeviceBean;
import net.gdface.facelog.db.DeviceGroupBean;
import net.gdface.facelog.db.ErrorLogBean;
import net.gdface.facelog.db.FaceBean;
import net.gdface.facelog.db.FeatureBean;
import net.gdface.facelog.db.IDeviceGroupManager;
import net.gdface.facelog.db.IDeviceManager;
import net.gdface.facelog.db.IFeatureManager;
import net.gdface.facelog.db.ILogManager;
import net.gdface.facelog.db.IPermitManager;
import net.gdface.facelog.db.IPersonGroupManager;
import net.gdface.facelog.db.IPersonManager;
import net.gdface.facelog.db.ImageBean;
import net.gdface.facelog.db.LogBean;
import net.gdface.facelog.db.LogLightBean;
import net.gdface.facelog.db.PermitBean;
import net.gdface.facelog.db.PersonBean;
import net.gdface.facelog.db.PersonGroupBean;
import net.gdface.facelog.ServiceSecurityException;
import net.gdface.facelog.Token.TokenType;
import net.gdface.thrift.exception.ServiceRuntimeException;
import net.gdface.utils.BaseTypeTransformer;
import net.gdface.utils.CollectionUtils;
import net.gdface.utils.BinaryUtils;
import net.gdface.facelog.DuplicateRecordException;
import net.gdface.facelog.TokenMangement.Enable;
import redis.clients.jedis.exceptions.JedisException;

/**
 * IFaceLog 服务实现
 * @author guyadong
 *
 */
public class FaceLogImpl implements IFaceLog,ServiceConstant {
	/** redis 服务器管理对象，负责初始化全局连接池对象，要放在redis lisetner对象初始化前完成初始化 */
	private final RedisManagement rm = new RedisManagement();
	/** 数据库操作对象，提供所有数据库访问 */
	private final DaoManagement dm = new DaoManagement();
	/** 令牌管理模块对象 */
	private final TokenMangement tm = new TokenMangement(dm);
	private final MessageQueueManagement mm = new MessageQueueManagement(rm, dm);
	private final FuzzyMatchManagement fmm = new FuzzyMatchManagement(dm);
	private final DtalkCmd dc = new DtalkCmd(rm.getRedisParameters(), tm, mm);
	private final DtalkServiceTaskDispatcher dt = new DtalkServiceTaskDispatcher(tm, mm);
	private final FaceApiServiceManagement fm =new FaceApiServiceManagement(dt, rm, dm);
	private final TempPwdManagement tp = new TempPwdManagement(dm);
	private final TokenValidatorPersonListener tokenValidatorPersonListener = new TokenValidatorPersonListener(dm);
	private final TokenValidatorPersonGroupListener tokenValidatorPersonGroupListener = new TokenValidatorPersonGroupListener(dm);
	private final TokenValidatorDeviceListener tokenValidatorDeviceListener = new TokenValidatorDeviceListener(dm);
	private final TokenValidatorDeviceGroupListener tokenValidatorDeviceGroupListener = new TokenValidatorDeviceGroupListener(dm);

	private final MQPersonListener mqPersonListener = new MQPersonListener(dm);
	private final MQPersonGroupListener mqPersonGroupListener = new MQPersonGroupListener(dm);
	private final MQDeviceListener mqDeviceListener = new MQDeviceListener(dm);
	private final MQDeviceGroupListener mqDeviceGroupListener = new MQDeviceGroupListener(dm);
	private final MQFeatureListener mqFeatureListener = new MQFeatureListener();
	private final MQPermitListener mqPermitListener = new MQPermitListener(dm);
	private final MQLogListener mqLogListener = new MQLogListener(rm.getRedisParameters().get(MQParam.LOG_MONITOR_CHANNEL));

	private final BaseTypeTransformer typeTransformer = new BaseTypeTransformer();
	//private final RedisLogConsumer redisLogConsumer  = new RedisLogConsumer(this);
	static{
		LocalTokenContextOp.initCurrentTokenContextOp();
	}
	/**
	 * 构造方法
	 */
	public FaceLogImpl() {
		initListener();
		fm.init();
		fmm.init();
	}
	private void initListener(){
		// 注册安全验证侦听器
		instanceOf(IPersonManager.class).registerListener(tokenValidatorPersonListener);
		instanceOf(IPersonGroupManager.class).registerListener(tokenValidatorPersonGroupListener);
		instanceOf(IDeviceManager.class).registerListener(tokenValidatorDeviceListener);
		instanceOf(IDeviceGroupManager.class).registerListener(tokenValidatorDeviceGroupListener);

		// 注册REDIS侦听器
		instanceOf(IPersonManager.class).registerListener(mqPersonListener);
		instanceOf(IPersonGroupManager.class).registerListener(mqPersonGroupListener);
		instanceOf(IDeviceManager.class).registerListener(mqDeviceListener);
		instanceOf(IDeviceGroupManager.class).registerListener(mqDeviceGroupListener);
		instanceOf(IFeatureManager.class).registerListener(mqFeatureListener);
		instanceOf(IPermitManager.class).registerListener(mqPermitListener);
		if(CONFIG.getBoolean(MONITOR_LOG)){
			instanceOf(ILogManager.class).registerListener(mqLogListener);
		}

		// 注册系统日志侦听器
		instanceOf(IPersonManager.class).registerListener(BaseSysLogLisener.PERSON_LOG_LISTENER);
		instanceOf(IPersonGroupManager.class).registerListener(BaseSysLogLisener.PERSON_GROUP_LOG_LISTENER);
		instanceOf(IDeviceManager.class).registerListener(BaseSysLogLisener.DEVICE_LOG_LISTENER);
		instanceOf(IDeviceGroupManager.class).registerListener(BaseSysLogLisener.DEVICE_GROUP_LOG_LISTENER);
		instanceOf(IPermitManager.class).registerListener(BaseSysLogLisener.PERMIT_LOG_LISTENER);
	}

	////////////////////////////////////////////////////////////////////////////////////
	/**
	 * 将封装在{@link RuntimeException}中的{@link ServiceSecurityException}剥离出来封装到{@link ServiceRuntimeException}
	 * @param error
	 * @return no return
	 * @see #throwServiceException(RuntimeException)
	 */
	protected static final ServiceRuntimeException wrapServiceRuntimeException(Exception error){
		try{
			if(error instanceof RuntimeException){
				throwServiceException((RuntimeException)error);
			}else 
				throw error;
			// dead code
			return new ServiceRuntimeException(error); 
		} catch(ServiceSecurityException e){
			return new ServiceRuntimeException(ExceptionType.SECURITY_ERROR.ordinal(),e);
		} catch(ServiceRuntimeException e){
			return e;
		} catch (Exception e) {
			return new ServiceRuntimeException(error);
		}
	}
	/**
	 * 将封装在{@link Exception}中的{@link ServiceSecurityException}剥离出来单独抛出<br>
	 * 将其他的{@link Exception}封装在{@link ServiceRuntimeException}抛出，
	 * @param error
	 * @return no return
	 * @throws ServiceSecurityException
	 */
	protected static final <T> T throwServiceException(RuntimeException error) 
			throws ServiceSecurityException{
		if(null != error.getCause()){
			try{
				throw error.getCause();
			}catch(ServiceSecurityException e){
				throw e;
			} catch (Throwable e) {
				// do nothing
			}
		}
		if(error instanceof RuntimeDaoException){
			if(error.getCause() instanceof ServiceSecurityException){
				throw (ServiceSecurityException)error.getCause();
			}
			throw new ServiceRuntimeException(ExceptionType.DAO.ordinal(),error); 
		}else if(error instanceof JedisException){
			throw new ServiceRuntimeException(ExceptionType.REDIS_ERROR.ordinal(),error);
		}else if(error instanceof FaceApiRuntimeException){
			throw new ServiceRuntimeException(ExceptionType.FACEAPI_ERROR.ordinal(),error);
		}
		throw new ServiceRuntimeException(error); 
	}
	
	protected static final <T extends Exception> void throwCauseIfInstanceOf(Exception error,Class<T> expType) throws T {
		if(null != error.getCause()){
			Throwables.throwIfInstanceOf(error.getCause(),expType);
		}
	}

	private static Date toDate(String date) throws ParseException{
		if(Strings.isNullOrEmpty(date)){
			return null;
		}
		try {
			return new SimpleDateFormat(ISO8601_FORMATTER_STR).parse(date);
		} catch (ParseException e) {
			try {
				return new SimpleDateFormat(TIMESTAMP_FORMATTER_STR).parse(date);
			} catch (ParseException e2) {
				return new SimpleDateFormat(DATE_FORMATTER_STR).parse(date);
			}
		}
	}
	@Override
	public PersonBean getPerson(int personId) {
		try{
			return desensitize(dm.daoGetPerson(personId));
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	@Override
	public List<PersonBean> getPersons(List<Integer> idList) {
		try{
			return desensitize(dm.daoGetPersons(idList));
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	@Override
	public PersonBean getPersonByPapersNum(String papersNum)  {
		try{
			return desensitize(dm.daoGetPersonByIndexPapersNum(papersNum));
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	@Override
	public PersonBean getPersonByMobilePhone(String mobilePhone)  {
		try{
			return desensitize(dm.daoGetPersonByIndexMobilePhone(mobilePhone));
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	
	@Override
	public PersonBean getPerson(int personId,Token token) {
		try{
			Enable.ALL.check(tm, token);
			tm.checkPermissionForPerson(token, personId);
			return dm.daoGetPerson(personId);
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	@Override
	public List<PersonBean> getPersons(List<Integer> idList,Token token) {
		try{
			Enable.ALL.check(tm, token);
			tm.checkPermissionForPerson(token, idList);
			return dm.daoGetPersons(idList);
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	@Override
	public PersonBean getPersonByPapersNum(String papersNum,Token token)  {
		try{
			Enable.ALL.check(tm, token);
			tm.checkPermissionForPerson(token, papersNum);
			return dm.daoGetPersonByIndexPapersNum(papersNum);
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	@Override
	public PersonBean getPersonByMobilePhone(String mobilePhone,Token token)  {
		try{
			Enable.ALL.check(tm, token);
			tm.checkPermissionForPersonByMobilePhone(token, mobilePhone);
			return dm.daoGetPersonByIndexMobilePhone(mobilePhone);
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	@Override
	public List<Integer> getPersonsPermittedOnDevice(int deviceId,boolean ignoreSchedule,List<Integer> excludePersonIds, Long timestamp) {
		try{
			Set<PersonBean> features = dm.daoGetPersonsPermittedOnDevice(deviceId, ignoreSchedule,  excludePersonIds, timestamp);
			return Lists.newArrayList(Collections2.transform(features, dm.daoCastPersonToPk));
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		}
	}

	@Override
	public List<String> getPersonsPermittedOnDeviceByGroup(int deviceId, boolean ignoreSchedule, List<Integer> excludePersonIds, Long timestamp) {
		try{
			return dm.daoGetPersonsPermittedOnDeviceByGroupId(deviceId, ignoreSchedule, excludePersonIds, timestamp);
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	
	@Override
	public List<PersonDataPackage> 
	loadPersonDataPackagesPermittedOnDevice(int deviceId, boolean ignoreSchedule, List<Integer> excludePersonIds, Long timestamp,String sdkVersion){
		try{
			return dm.daoLoadPersonDataPackagesPermittedOnDevice(deviceId, ignoreSchedule,  excludePersonIds, timestamp,sdkVersion);
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	@Override
	public List<PersonDataPackage> loadPersonDataPackages(List<Integer> personIds,String sdkVersion,int deviceId){
		try{
			return dm.daoLoadPersonDataPackages(personIds, sdkVersion, deviceId);
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		}
	}

	@Override
	public List<PersonDataPackage> loadPersonDataPackagesInSameGroup(List<Integer> personIds,String sdkVersion,int deviceId){
		try{
			return dm.daoLoadPersonDataPackagesInSameGroup(personIds, sdkVersion, deviceId);
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		}
	}

	@Override
	public int deletePerson(final int personId, Token token) {
		try{
			Enable.PERSON_ONLY.check(tm, token);
			tm.checkPermissionForPerson(token, personId);
			return BaseDao.daoRunAsTransaction(new Callable<Integer>(){
				@Override
				public Integer call() throws Exception {
					return dm.daoDeletePerson(personId);
				}});
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		} 
	}

	@Override
	public int deletePersons(final List<Integer> personIdList, Token token) {
		try{
			Enable.PERSON_ONLY.check(tm, token);
			tm.checkPermissionForPerson(token, personIdList);
			return BaseDao.daoRunAsTransaction(new Callable<Integer>(){
				@Override
				public Integer call() throws Exception {
					return dm.daoDeletePersonsByPrimaryKey(personIdList);
				}});
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		} 
	}

	@Override
	public int deletePersonByPapersNum(final String papersNum, Token token)  {
		try{	
			Enable.PERSON_ONLY.check(tm, token);
			tm.checkPermissionForPerson(token, papersNum);
			return BaseDao.daoRunAsTransaction(new Callable<Integer>(){
				@Override
				public Integer call() throws Exception {
					return dm.daoDeletePersonByPapersNum(papersNum);
				}});
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		}
	}

	@Override
	public  int deletePersonsByPapersNum(final List<String> papersNumList, Token token) {
		try{		
			Enable.PERSON_ONLY.check(tm, token);
			tm.checkPermissionForPerson(token, papersNumList);
			return BaseDao.daoRunAsTransaction(new Callable<Integer>(){
				@Override
				public Integer call() throws Exception {
					return dm.daoDeletePersonByPapersNum(papersNumList);
				}});
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	@Override
	public int deletePersonByMobilePhone(final String mobilePhone, Token token)  {
		try{	
			Enable.PERSON_ONLY.check(tm, token);
			tm.checkPermissionForPersonByMobilePhone(token, mobilePhone);
			return BaseDao.daoRunAsTransaction(new Callable<Integer>(){
				@Override
				public Integer call() throws Exception {
					return dm.daoDeletePersonByMobilePhone(mobilePhone);
				}});
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	@Override
	public  int deletePersonsByMobilePhone(final List<String> mobilePhoneList, Token token) {
		try{		
			Enable.PERSON_ONLY.check(tm, token);
			tm.checkPermissionForPersonByMobilePhone(token, mobilePhoneList);
			return BaseDao.daoRunAsTransaction(new Callable<Integer>(){
				@Override
				public Integer call() throws Exception {
					return dm.daoDeletePersonsByMobilePhone(mobilePhoneList);
				}});
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	@Override
	public boolean existsPerson(int persionId) {
		try{
			return dm.daoExistsPerson(persionId);
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		}
	}

	@Override
	public boolean isDisable(int personId){
		try{
			return dm.daoIsDisable(personId);
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		}
	}

	@Override
	public void disablePerson(final int personId, final Integer moveToGroupId, 
			final boolean deletePhoto, final boolean deleteFeature, final boolean deleteLog, Token token){
		try{
			Enable.PERSON_ONLY.check(tm, token);
			tm.checkPermissionForPerson(token, personId);
			BaseDao.daoRunAsTransaction(new Runnable(){
				@Override
				public void run() {
					dm.daoDisablePerson(personId, moveToGroupId, deletePhoto, deleteFeature, deleteLog);
				}});
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		}
	}

	@Override
	public void setPersonExpiryDate(int personId,long expiryDate, Token token){
		try{
			Enable.PERSON_ONLY.check(tm, token);
			tm.checkPermissionForPerson(token, personId);
			dm.daoSetPersonExpiryDate(dm.daoGetPerson(personId),new Date(expiryDate));
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	@Override
	public void setPersonExpiryDate(int personId,String expiryDate, Token token){
		try{
			Enable.PERSON_ONLY.check(tm, token);
			tm.checkPermissionForPerson(token, personId);
			dm.daoSetPersonExpiryDate(dm.daoGetPerson(personId),toDate(checkNotNull(expiryDate,"timestamp is null")));
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	@Override
	public  void setPersonExpiryDate(final List<Integer> personIdList,final long expiryDate, Token token){
		try{		
			Enable.PERSON_ONLY.check(tm, token);
			tm.checkPermissionForPerson(token, personIdList);
			BaseDao.daoRunAsTransaction(new Runnable(){
				@Override
				public void run() {
					dm.daoSetPersonExpiryDate(personIdList,new Date(expiryDate));
				}});			
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		}
	}

	@Override
	public  void disablePerson(final List<Integer> personIdList, Token token){
		setPersonExpiryDate(personIdList,System.currentTimeMillis(), token);
	}

	@Override
	public List<LogBean> getLogBeansByPersonId(int personId) {
		try{
			return dm.daoGetLogBeansByPersonIdOnPerson(personId);
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	@Override
	public List<LogBean> getLogBeansByPersonId(int personId,Token token) {
		try{
			Enable.ALL.check(tm, token);
			tm.checkPermissionForPerson(token, personId);
			return dm.daoGetLogBeansByPersonIdOnPerson(personId);
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		}
	}

	@Override
	public List<Integer> loadAllPerson() {
		try{
			return dm.daoLoadPersonIdByWhere(null);
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		} 
	}
	
	@Override
	public List<Integer> loadAllPerson(Token token) {
		return loadPersonIdByWhere(null,token);
	}

	@Override
	public List<Integer> loadPersonIdByWhere(String where) {
		try{
			return dm.daoLoadPersonIdByWhere(where);
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		} 
	}
	@Override
	public List<Integer> loadPersonIdByWhere(String where, Token token) {
		try{
			Enable.ALL.check(tm, token);
			return dm.daoLoadPersonIdByWhere(tm.normailizeWhereForPerson(token,where));
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		} 
	}
	@Override
	public List<PersonBean> loadPersonByWhere(String where, int startRow, int numRows)  {
		try{
			return desensitize(dm.daoLoadPersonByWhere(where, startRow, numRows));
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	@Override
	public List<PersonBean> loadPersonByWhere(String where, int startRow, int numRows,Token token)  {
		try{
			Enable.ALL.check(tm, token);
			return dm.daoLoadPersonByWhere(tm.normailizeWhereForPerson(token,where), startRow, numRows);
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	@Override
	public int countPersonByWhere(String where) {
		try{
			return dm.daoCountPersonByWhere(where);
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	@Override
	public int countPersonByWhere(String where,Token token) {
		try{
			Enable.ALL.check(tm, token);
			return dm.daoCountPersonByWhere(tm.normailizeWhereForPerson(token,where));
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	@Override
	public PersonBean savePerson(PersonBean personBean, Token token) {
		try{
			Enable.ALL.check(tm, token);
			tm.checkPermissionForPerson(token, personBean);
			return dm.daoSavePerson(personBean);
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		}
	}

	@Override
	public void savePersons(List<PersonBean> persons, Token token)  {
		try{
			Enable.PERSON_ONLY.check(tm, token);
			tm.checkPermissionForPerson(token, persons);
			dm.daoSavePersonsAsTransaction(persons);
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		}
	}

	@Override
	public PersonBean savePerson(final PersonBean personBean, final byte[] idPhoto, final Token token) {
		try{
			Enable.ALL.check(tm, token);
			tm.checkPermissionForPerson(token, personBean);
			checkArgument(null != personBean, "personBean is null");
			return BaseDao.daoRunAsTransaction(new Callable<PersonBean>(){
				@Override
				public PersonBean call() throws Exception {
					return dm.daoSavePerson(personBean, BinaryUtils.getByteBuffer(idPhoto), null,tm.getDeviceOrNull(token));
				}});
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	@Override
	public PersonBean savePerson(final PersonBean personBean, final byte[] idPhoto,final boolean exractFeature, final Token token) {
		try{
			Enable.ALL.check(tm, token);
			tm.checkPermissionForPerson(token, personBean);
			checkArgument(null != personBean, "personBean is null");
			return BaseDao.daoRunAsTransaction(new Callable<PersonBean>(){
				@Override
				public PersonBean call() throws Exception {
					FeatureBean featureBean = idPhoto != null && exractFeature ? fm.detectMaxFaceAndGetFeature(idPhoto) : null;
					return dm.daoSavePerson(personBean, BinaryUtils.getByteBuffer(idPhoto), featureBean,tm.getDeviceOrNull(token));
				}});
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		}
	}

	@Override
	public int savePersons(final List<byte[]> photos, final List<PersonBean> persons, Token token) {
		try{
			Enable.PERSON_ONLY.check(tm, token);
			tm.checkPermissionForPerson(token, persons);
			final List<ByteBuffer> buffers = Lists.transform(photos, typeTransformer.getTransformer(byte[].class, ByteBuffer.class));
			return BaseDao.daoRunAsTransaction(new Callable<Integer>(){
				@Override
				public Integer call() throws Exception {
					return dm.daoSavePerson(CollectionUtils.merge(buffers, persons));
				}});
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		}
	}

	@Override
	public PersonBean savePerson(final PersonBean personBean, final String idPhotoMd5, final String featureMd5, Token token)
			 {
		try {
			Enable.ALL.check(tm, token);
			tm.checkPermissionForPerson(token, personBean);
			checkArgument(null != personBean, "personBean is null");
			return BaseDao.daoRunAsTransaction(new Callable<PersonBean>() {
				@Override
				public PersonBean call() throws Exception {
					FeatureBean featureBean = dm.daoGetFeature(featureMd5);
					return dm.daoSavePerson(personBean, dm.daoGetImage(idPhotoMd5), 
							featureBean == null ? null : Arrays.asList(featureBean));
				}
			});
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	
	@Override
	public PersonBean savePerson(final PersonBean personBean, final byte[] idPhoto, final FeatureBean featureBean,
			final Token token)  {
		try {
			Enable.ALL.check(tm, token);
			tm.checkPermissionForPerson(token, personBean);
			checkArgument(null != personBean, "personBean is null");
			return BaseDao.daoRunAsTransaction(new Callable<PersonBean>() {
				@Override
				public PersonBean call() throws Exception {
					return dm.daoSavePerson(personBean, 
							BinaryUtils.getByteBuffer(idPhoto), 
							featureBean, 
							tm.getDeviceOrNull(token));
				}
			});
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		}
	}

	@Override
	public PersonBean savePerson(final PersonBean personBean, final byte[] idPhoto, final byte[] feature,
			final String featureVersion, final List<FaceBean> faceBeans, final Token token)  {
		try {
			Enable.ALL.check(tm, token);
			tm.checkPermissionForPerson(token, personBean);
			checkArgument(null != personBean, "personBean is null");
			checkArgument(null != feature, "feature is null");
			return BaseDao.daoRunAsTransaction(new Callable<PersonBean>() {
				@Override
				public PersonBean call() throws Exception {
					return dm.daoSavePerson(personBean, BinaryUtils.getByteBuffer(idPhoto), 
							dm.daoAddFeature(BinaryUtils.getByteBuffer(feature), 
									featureVersion, personBean, faceBeans), 
							tm.getDeviceOrNull(token));
				}
			});
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		}
	}

	@Override
	public PersonBean savePerson(final PersonBean personBean, final byte[] idPhoto, final byte[] feature,
			final String featureVersion, List<byte[]> photos, final List<FaceBean> faces, final Token token)  {
		try {
			Enable.ALL.check(tm, token);
			tm.checkPermissionForPerson(token, personBean);
			checkArgument(null != personBean, "personBean is null");
			checkArgument(null != feature, "feature is null");
			final List<ByteBuffer> buffers = Lists.transform(photos, typeTransformer.getTransformer(byte[].class, ByteBuffer.class));
			return BaseDao.daoRunAsTransaction(new Callable<PersonBean>() {
				@Override
				public PersonBean call() throws Exception {
					return dm.daoSavePerson(personBean, 
							BinaryUtils.getByteBuffer(idPhoto), 
							BinaryUtils.getByteBuffer(feature), 
							featureVersion, 
							CollectionUtils.merge(buffers, faces), 
							tm.getDeviceOrNull(token));
				}
			});
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		}
	}

	@Override
	public PersonBean savePerson(final PersonBean personBean, final byte[] idPhoto, final byte[] feature,
			final String featureVersion, final byte[] featureImage, final FaceBean faceBean, final Token token) {
		try{
			Enable.ALL.check(tm, token);
			tm.checkPermissionForPerson(token, personBean);
			checkArgument(null != personBean,"personBean is null");
			// 避免idPhoto,featureImage相同抛出异常
			final byte[] fimg=(idPhoto != null && featureImage != null && Arrays.equals(idPhoto, featureImage)) 
										? null : featureImage;
			return BaseDao.daoRunAsTransaction(new Callable<PersonBean>(){
				@Override
				public PersonBean call() throws Exception {
					return dm.daoSavePerson(personBean,
							BinaryUtils.getByteBuffer(idPhoto),
							BinaryUtils.getByteBuffer(feature),
							featureVersion,
							BinaryUtils.getByteBuffer(fimg),
							faceBean, 
							tm.getDeviceOrNull(token));
				}});
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		}
	}

	@Override
	public void replaceFeature(final int personId, final String featureMd5, final boolean deleteOldFeatureImage, Token token)	{
		try {
			Enable.ALL.check(tm, token);
			tm.checkPermissionForPerson(token, personId);
			BaseDao.daoRunAsTransaction(new Runnable() {
				@Override
				public void run() {
					dm.daoReplaceFeature(personId, featureMd5, deleteOldFeatureImage);
				}
			});
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		}
	}

	@Override
	public List<Integer> loadUpdatedPersons(long timestamp) {
		try{
			return dm.daoLoadUpdatedPersons(new Date(timestamp), null, null);
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	@Override
	public List<Integer> loadUpdatedPersons(long timestamp,Token token) {
		try{
			Enable.ALL.check(tm, token);
			return dm.daoLoadUpdatedPersons(new Date(timestamp), 
					tm.normailizeWhereForPerson(token, null), 
					tm.normailizeWhereForFeature(token, null));
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	@Override
	public List<Integer> loadUpdatedPersons(String timestamp) {
		try{
			return dm.daoLoadUpdatedPersons(toDate(timestamp), null, null);
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	@Override
	public List<Integer> loadUpdatedPersons(String timestamp, Token token) {
		try{
			Enable.ALL.check(tm, token);
			return dm.daoLoadUpdatedPersons(toDate(timestamp), 
					tm.normailizeWhereForPerson(token, null), 
					tm.normailizeWhereForFeature(token, null));
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	@Override
	public List<Integer> loadPersonIdByUpdateTime(long timestamp) {
		try{
			return dm.daoLoadPersonIdByUpdateTime(null,new Date(timestamp));
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	@Override
	public List<Integer> loadPersonIdByUpdateTime(long timestamp, Token token) {
		try{
			Enable.ALL.check(tm, token);
			return dm.daoLoadPersonIdByUpdateTime(tm.normailizeWhereForPerson(token, null),
					new Date(timestamp));
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	
	@Override
	public List<Integer> loadPersonIdByUpdateTime(String timestamp) {
		try{
			return dm.daoLoadPersonIdByUpdateTime(null,toDate(checkNotNull(timestamp,"timestamp is null")));
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	@Override
	public List<Integer> loadPersonIdByUpdateTime(String timestamp, Token token) {
		try{
			Enable.ALL.check(tm, token);
			return dm.daoLoadPersonIdByUpdateTime(
					tm.normailizeWhereForPerson(token, null),
					toDate(checkNotNull(timestamp,"timestamp is null")));
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	@Override
	public List<String> loadFeatureMd5ByUpdate(long timestamp) {
		try{		
			return dm.daoLoadFeatureMd5ByUpdateTime(null, new Date(timestamp));
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	@Override
	public List<String> loadFeatureMd5ByUpdate(long timestamp, Token token) {
		try{
			Enable.ALL.check(tm, token);
			return dm.daoLoadFeatureMd5ByUpdateTime(
					tm.normailizeWhereForFeature(token, null), 
					new Date(timestamp));
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		}
	}

	@Override
	public List<String> loadFeatureMd5ByUpdate(String timestamp) {
		try{		
			return dm.daoLoadFeatureMd5ByUpdateTime(null, toDate(checkNotNull(timestamp,"timestamp is null")));
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	
	@Override
	public List<String> loadFeatureMd5ByUpdate(String timestamp, Token token) {
		try{
			Enable.ALL.check(tm, token);
			return dm.daoLoadFeatureMd5ByUpdateTime(
					tm.normailizeWhereForFeature(token, null), 
					toDate(checkNotNull(timestamp,"timestamp is null")));
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		}
	}

	@Override
	public void addLog(LogBean logBean, Token token)throws DuplicateRecordException {
		try{
			Enable.DEVICE_ONLY.check(tm, token);
			tm.checkPermissionForLog(token, logBean);
			checkArgument(logBean != null,"logBean is null");
			dm.daoAddLog(logBean);
		} catch (Exception e) {
			Throwables.throwIfInstanceOf(e, DuplicateRecordException.class);
			throw wrapServiceRuntimeException(e);
		}
	}

	@Override
	public void addLog(final LogBean logBean,final FaceBean faceBean,final byte[] featureImage,Token token) throws DuplicateRecordException {
		try{
			Enable.DEVICE_ONLY.check(tm, token);
			tm.checkPermissionForLog(token, logBean);
			checkArgument(logBean != null,"logBean is null");
			checkArgument(faceBean != null,"faceBean is null");
			checkArgument(featureImage != null,"featureImage is null");
			BaseDao.daoRunAsTransaction(new Callable<LogBean>() {
				@Override
				public LogBean call() throws Exception {
					return dm.daoAddLog(logBean, 
							faceBean, 
							BinaryUtils.getByteBufferNotEmpty(featureImage));
				}});
		} catch (Exception e) {
			throwCauseIfInstanceOf(e,DuplicateRecordException.class);
			throw wrapServiceRuntimeException(e);
		}
	}
	@Override
	public void addLogs(final List<LogBean> logBeans,final List<FaceBean> faceBeans,final List<byte[]> featureImages,Token token) throws DuplicateRecordException {
		try{
			Enable.DEVICE_ONLY.check(tm, token);
			tm.checkPermissionForLog(token, logBeans);
			checkArgument(logBeans != null,"logBeans is null");
			checkArgument(faceBeans != null,"faceBeans is null");
			checkArgument(featureImages != null,"featureImages is null");
			checkArgument(logBeans.size() == faceBeans.size() && logBeans.size() == featureImages.size(),
					"size of logBeans,faceBeans,featureImages must be same");
			BaseDao.daoRunAsTransaction(new Callable<Integer>() {

				@Override
				public Integer call() throws Exception {
					for(int i = 0; i<logBeans.size(); ++i){
						dm.daoAddLog(
								checkNotNull(logBeans.get(i),"logBeans has null element [%s]",i), 
								checkNotNull(faceBeans.get(i),"faceBeans has null element [%s]",i), 
								BinaryUtils.getByteBufferNotEmpty(checkNotNull(featureImages.get(i),"featureImages has null element [%s]",i)));
					}
					return logBeans.size();
				}
			});
		} catch (Exception e) {
			throwCauseIfInstanceOf(e,DuplicateRecordException.class);
			throw wrapServiceRuntimeException(e);
		}
	}
	@Override
	public void addLogs(List<LogBean> logBeans, Token token)throws DuplicateRecordException {
		try{
			Enable.DEVICE_ONLY.check(tm, token);
			tm.checkPermissionForLog(token, logBeans);
			dm.daoAddLogsAsTransaction(logBeans);
		} catch (Exception e) {
			Throwables.throwIfInstanceOf(e,DuplicateRecordException.class);
			throw wrapServiceRuntimeException(e);
		}
	}
	@Override
	public void addLog(final LogBean logBean,final byte[] faceImage,final Token token) throws DuplicateRecordException {
		try{
			Enable.DEVICE_ONLY.check(tm, token);
			tm.checkPermissionForLog(token, logBean);
			checkArgument(faceImage != null,"faceImage is null");
			BaseDao.daoRunAsTransaction(new Callable<LogBean>() {
				@Override
				public LogBean call() throws Exception {
					return dm.daoAddLog(logBean,BinaryUtils.getByteBufferNotEmpty(faceImage),token.getId());
				}});
		} catch (Exception e) {
			throwCauseIfInstanceOf(e,DuplicateRecordException.class);
			throw wrapServiceRuntimeException(e);
		}
	}
	@Override
	public void addLogs(List<LogBean> logBeans,final List<byte[]> faceImages,final Token token) throws DuplicateRecordException {
		try{
			Enable.DEVICE_ONLY.check(tm, token);
			tm.checkPermissionForLog(token, logBeans);
			checkArgument(faceImages != null,"faceImages is null");
			final List<LogBean> logs;
			if(logBeans == null){
				logs = Arrays.asList(new LogBean[faceImages.size()]);
			}else{
				logs = logBeans;
			}
			checkArgument(logs.size() == faceImages.size(), "size of logBeans,faceImages must be same");
			BaseDao.daoRunAsTransaction(new Callable<Integer>() {

				@Override
				public Integer call() throws Exception {
					for(int i = 0; i<faceImages.size(); ++i){
						dm.daoAddLog(								 
							logs.get(i),
							BinaryUtils.getByteBufferNotEmpty(checkNotNull(faceImages.get(i),"featureImages has null element [%s]",i)),
							token.getId());
					}
					return faceImages.size();
				}
			});
		} catch (Exception e) {
			throwCauseIfInstanceOf(e,DuplicateRecordException.class);
			throw wrapServiceRuntimeException(e);
		}
	}
	@Override
	public List<LogBean> loadLogByWhere(String where, int startRow, int numRows)  {
		try{
			return dm.daoLoadLogByWhere(where, startRow, numRows);
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		} 
	}
	@Override
	public List<LogBean> loadLogByWhere(String where, int startRow, int numRows,Token token)  {
		try{
			Enable.ALL.check(tm, token);			
			return dm.daoLoadLogByWhere(tm.normailizeWhereForLog(token, where), startRow, numRows);
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		} 
	}

	@Override
	public List<LogLightBean> loadLogLightByWhere(String where, int startRow, int numRows)  {
		try{
			return dm.daoLoadLogLightByWhere(where, startRow, numRows);
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		} 
	}
	@Override
	public List<LogLightBean> loadLogLightByWhere(String where, int startRow, int numRows, Token token)  {
		try{
			Enable.ALL.check(tm, token);
			return dm.daoLoadLogLightByWhere(tm.normailizeWhereForLogLight(token, where), startRow, numRows);
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		} 
	}

	@Override
	public int countLogLightByWhere(String where)  {
		try{         
			return dm.daoCountLogLightByWhere(where);
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		} 
	}
	@Override
	public int countLogLightByWhere(String where,Token token)  {
		try{         
			Enable.ALL.check(tm, token);
			return dm.daoCountLogLightByWhere(tm.normailizeWhereForLogLight(token, where));
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		} 
	}

	@Override
	public int countLogByWhere(String where)  {
		try{
			return dm.daoCountLogByWhere(where);
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		} 
	}
	@Override
	public int countLogByWhere(String where,Token token)  {
		try{
			Enable.ALL.check(tm, token);
			return dm.daoCountLogByWhere(tm.normailizeWhereForLog(token, where));
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		} 
	}
	@Override
	public Map<String, Integer> countPersonLog(int personId,Long startDate,Long endDate)  {
		try{
			return dm.daoCountPersonLog(personId,startDate == null ? null : new Date(startDate),endDate == null ? null : new Date(endDate));
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		} 
	}
	@Override
	public Map<String, Integer> countPersonLog(int personId,Long startDate,Long endDate,Token token)  {
		try{
			Enable.ALL.check(tm, token);
			tm.checkPermissionForPerson(token, personId);
			return dm.daoCountPersonLog(personId,startDate == null ? null : new Date(startDate),endDate == null ? null : new Date(endDate));
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		} 
	}
	@Override
	public Map<String, Integer> countPersonLog(int personId,String startDate,String endDate)  {
		try{
			return dm.daoCountPersonLog(personId,toDate(startDate),toDate(endDate));
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	@Override
	public Map<String, Integer> countPersonLog(int personId,String startDate,String endDate,Token token)  {
		try{
			Enable.ALL.check(tm, token);
			tm.checkPermissionForPerson(token, personId);
			return dm.daoCountPersonLog(personId,toDate(startDate),toDate(endDate));
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	@Override
	public int deleteLogByWhere(String where,Token token){
		try{
			Enable.ROOT_ONLY.check(tm, token);			
			checkArgument(where != null && where.toUpperCase().startsWith("WHERE"),"WHERE clause required");
			return dm.logManager.deleteByWhere(where);
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		} 		
	}
	@Override
    public List<LogLightBean> loadLogLightByVerifyTime(long timestamp,int startRow, int numRows){
		try{
			return dm.daoLoadLogLightByVerifyTime(null,new Date(timestamp),startRow,numRows);
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		}
    }
	@Override
	public List<LogLightBean> loadLogLightByVerifyTime(long timestamp,int startRow, int numRows, Token token){
		try{
			Enable.ALL.check(tm, token);
			return dm.daoLoadLogLightByVerifyTime(tm.normailizeWhereForLogLight(token, null),
					new Date(timestamp),startRow,numRows);
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	
    @Override
	public List<LogLightBean> loadLogLightByVerifyTime(String timestamp,int startRow, int numRows){
		try{
			return dm.daoLoadLogLightByVerifyTime(null, toDate(checkNotNull(timestamp,"timestamp is null")),startRow,numRows);
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		}
    }
    @Override
	public List<LogLightBean> loadLogLightByVerifyTime(String timestamp,int startRow, int numRows,Token token){
    	try{
    		Enable.ALL.check(tm, token);
    		return dm.daoLoadLogLightByVerifyTime(tm.normailizeWhereForLogLight(token, null),toDate(checkNotNull(timestamp,"timestamp is null")),
    				startRow,numRows);
    	} catch (Exception e) {
    		throw wrapServiceRuntimeException(e);
    	}
    }
	
    @Override
    public int countLogLightByVerifyTime(long timestamp){
		try{
			return dm.daoCountLogLightByVerifyTime(null,new Date(timestamp));
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		}
    }
    @Override
	public int countLogLightByVerifyTime(long timestamp,Token token){
    	try{
    		Enable.ALL.check(tm, token);
    		return dm.daoCountLogLightByVerifyTime(tm.normailizeWhereForLogLight(token, null),new Date(timestamp));
    	} catch (Exception e) {
    		throw wrapServiceRuntimeException(e);
    	}
    }
    @Override
	public int countLogLightByVerifyTime(String timestamp){
		try{
			return dm.daoCountLogLightByVerifyTime(null,toDate(checkNotNull(timestamp,"timestamp is null")));
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		}
    }
    @Override
	public int countLogLightByVerifyTime(String timestamp,Token token){
    	try{
    		Enable.ALL.check(tm, token);
    		return dm.daoCountLogLightByVerifyTime(tm.normailizeWhereForLogLight(token, null),
    				toDate(checkNotNull(timestamp,"timestamp is null")));
    	} catch (Exception e) {
    		throw wrapServiceRuntimeException(e);
    	}
    }
    
	@Override
	public void addErrorLog(ErrorLogBean errorLogBean, Token token) {
		try{
			Enable.ALL.check(tm, token);
			checkNotNull(errorLogBean,"logBean is null").setNew(true);
			dm.daoAddErrorLog(errorLogBean);
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	
	@Override
	public List<ErrorLogBean> loadErrorLogByWhere(String where, int startRow, int numRows)  {
		try{
			return dm.daoLoadErrorLogByWhere(where, startRow, numRows);
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		} 
	}
	@Override
	public int countErrorLogByWhere(String where){
		try{
			return dm.daoCountErrorLogByWhere(where);
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		} 
	}
	@Override
	public int deleteErrorLogByWhere(String where,Token token){
		try{
			Enable.ROOT_ONLY.check(tm, token);
			checkArgument(where != null && where.toUpperCase().startsWith("WHERE"),"WHERE clause required");
			return dm.errorLogManager.deleteByWhere(where);
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		} 		
	}
    @Override
	public boolean existsImage(String md5)  {
		try{
			return dm.daoExistsImage(md5);
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		}
	}

	@Override
	public ImageBean addImage(byte[] imageData,Integer deviceId
			, FaceBean faceBean , Integer personId, Token token) throws DuplicateRecordException{
		try{
			Enable.ALL.check(tm, token);
			tm.checkPermissionForDevice(token, deviceId);
			tm.checkPermissionForPerson(token, personId);
			checkArgument( null != imageData,"imageData is null");
			return dm.daoAddImage(BinaryUtils.getByteBuffer(imageData),
					dm.daoGetDevice(deviceId),Arrays.asList(faceBean),Arrays.asList(dm.daoGetPerson(personId)));		
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		} catch (ServiceSecurityException e) {
			throw new ServiceRuntimeException(ExceptionType.SECURITY_ERROR.ordinal(),e);
		} catch (IOException e) {
			throw new ServiceRuntimeException(ExceptionType.IMAGE_ERROR.ordinal(),e);
		} 
	}

    @Override
	public boolean existsFeature(String md5)  {
		try{
			return dm.daoExistsFeature(md5);
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		}
	}

    private static <C extends Iterable<FaceBean>> C initFace(C faces){
    	if(null != faces){
    		for(FaceBean f:faces){
    			// 删除 fl_face.feature_md5字段内容,DaoManagement.daoAddFeature中会自动填充此字段
    			// 不删除的话,数据库存储face记录时会抛出外键约束异常
    			f.setFeatureMd5(null);
    		}
    	}
		return faces;
    }
    private static List<FaceBean> initFace(FaceBean faceBean){
    	List<FaceBean> faceList = faceBean == null ? null : Arrays.asList(faceBean);
    	return initFace(faceList);
    }
	@Override
	public FeatureBean addFeature(final byte[] feature,
			final String featureVersion,
			final Integer personId, 
			final List<FaceBean> faecBeans, 
			final String removed, 
			Token token)
			throws DuplicateRecordException{
		try{
			Enable.ALL.check(tm, token);
			tm.checkPermissionForPerson(token, personId);
			checkArgument( null != feature,"feature is null");
			return BaseDao.daoRunAsTransaction(new Callable<FeatureBean>() {

				@Override
				public FeatureBean call() throws Exception {
					dm.daoDeleteFeatureChecked(removed,true);
					return dm.daoAddFeature(BinaryUtils.getByteBuffer(feature), 
							featureVersion, 
							dm.daoGetPerson(personId), 
							initFace(faecBeans));
				}
			});
		} catch (Exception e) {
			throwCauseIfInstanceOf(e,DuplicateRecordException.class);
			throw wrapServiceRuntimeException(e);
		} 
	}
	@Override
	public FeatureBean addFeature(final byte[] feature,
			final String featureVersion,
			final Integer personId,
			final boolean asIdPhotoIfAbsent,
			final byte[] featurePhoto,
			final FaceBean faceBean, 
			final String removed, 
			final Token token)throws DuplicateRecordException{
		try{
			Enable.ALL.check(tm, token);
			tm.checkPermissionForPerson(token, personId);
			checkArgument(feature != null,"feature is null");

			return BaseDao.daoRunAsTransaction(new Callable<FeatureBean>() {
				@Override
				public FeatureBean call() throws Exception {
					dm.daoDeleteFeatureChecked(removed,true);
					PersonBean personBean = dm.daoGetPerson(personId);
					List<FaceBean> faceList = initFace(faceBean);
					ImageBean imageBean = dm.daoAddImage(BinaryUtils.getByteBuffer(featurePhoto), 
							tm.getDeviceOrNull(token),faceList, null);
					if(personBean != null && imageBean != null 
							&& personBean.getImageMd5() == null && asIdPhotoIfAbsent){
						personBean.setImageMd5(imageBean.getMd5());
					}
					return dm.daoAddFeature(ByteBuffer.wrap(feature), featureVersion, personBean, faceList);					
				}
			});
		} catch (Exception e) {
			throwCauseIfInstanceOf(e,DuplicateRecordException.class);
			throw wrapServiceRuntimeException(e);
		} 
	}
	@Override
	public FeatureBean addFeature(final byte[] feature, 
			final String featureVersion, 
			final Integer personId,
			List<byte[]> photos, 
			final List<FaceBean> faces, 
			final String removed, 
			final Token token) throws DuplicateRecordException {
		try {
			Enable.ALL.check(tm, token);
			tm.checkPermissionForPerson(token, personId);
			checkArgument(feature != null,"feature is null");
			final List<ByteBuffer> buffers = Lists.transform(photos, typeTransformer.getTransformer(byte[].class, ByteBuffer.class));
			return BaseDao.daoRunAsTransaction(new Callable<FeatureBean>() {

				@Override
				public FeatureBean call() throws Exception {
					dm.daoDeleteFeatureChecked(removed,true);

					return dm.daoAddFeature(BinaryUtils.getByteBuffer(feature), 
							featureVersion, dm.daoGetPerson(personId), 
							CollectionUtils.merge(buffers, initFace(faces)), 
							tm.getDeviceOrNull(token));
				}
			});
		} catch (Exception e) {
			throwCauseIfInstanceOf(e,DuplicateRecordException.class);
			throw wrapServiceRuntimeException(e);
		} 
	}

	@Override
	public List<String> deleteFeature(final String featureMd5,final boolean deleteImage, Token token){
		try{
			Enable.ALL.check(tm, token);
			tm.checkPermissionForFeature(token, featureMd5);
			return BaseDao.daoRunAsTransaction(new Callable<List<String>>() {
				@Override
				public List<String> call() throws Exception {
					return dm.daoDeleteFeature(featureMd5,deleteImage);
				}
			});
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		}
	}

	@Override
	public int deleteAllFeaturesByPersonId(final int personId,final boolean deleteImage, Token token){
		try{
			Enable.ALL.check(tm, token);
			tm.checkPermissionForPerson(token, personId);
			return BaseDao.daoRunAsTransaction(new Callable<Integer>() {
				@Override
				public Integer call() throws Exception {
					return dm.daoDeleteAllFeaturesByPersonId(personId,deleteImage);
				}
			});
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		}
	}

	@Override
	public FeatureBean getFeature(String md5){
		try{
			return dm.daoGetFeature(md5);
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		}
	}

	@Override
	public List<FeatureBean> getFeatures(List<String> md5List){
		try{
			return dm.daoGetFeatures(md5List);
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	@Override
	public List<String> getFeaturesOfPerson(int personId){
		try{			
			return dm.daoToPrimaryKeyListFromFeatures(dm.daoGetFeatureBeansByPersonIdOnPerson(personId));
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		}
	}

	@Override
	public List<String> getFeaturesByPersonIdAndSdkVersion(int personId,String sdkVersion) {
		try{
			return dm.daoToPrimaryKeyListFromFeatures(dm.daoGetFeaturesByPersonIdAndSdkVersion(personId,sdkVersion));			
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		}
	}

	@Override
	public List<String> getFeaturesPermittedOnDevice(int deviceId,boolean ignoreSchedule, String sdkVersion,List<String> excludeFeatureIds, Long timestamp) {
		try{
			Set<FeatureBean> features = dm.daoGetFeaturesPermittedOnDevice(deviceId, ignoreSchedule, sdkVersion, excludeFeatureIds, timestamp);
			return dm.daoToPrimaryKeyListFromFeatures(Lists.newArrayList(features));
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		}
	}

	@Override
	public List<String> getFeaturesOfImage(String imageMd5){
		try {
			return dm.daoGetFeaturesOfImage(imageMd5);
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	@Override
	public byte[] getFeatureBytes(String md5){		
		try {
			return dm.daoGetFeatureBytes(md5, false);
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		} 
	}

	@Override
	public byte[] getFeatureBytes(String md5, boolean truncation){		
		try {
			return dm.daoGetFeatureBytes(md5, truncation);
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		} 
	}
	
	@Override
	public List<byte[]> getFeatureBytesList(List<String> md5List, boolean truncation){		
		try {
			return dm.daoGetFeatureBytes(md5List, truncation);
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		} 
	}
	
	@Override
	public byte[] getImageBytes(String imageMD5){
		try {
			return dm.daoGetImageBytes(imageMD5);
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	@Override
	public byte[] getImageBytes(String primaryKey,String refType){
		try {
			return dm.daoGetImageBytes(primaryKey, 
					RefSrcType.valueOf(MoreObjects.firstNonNull(refType,RefSrcType.DEFAULT.name())));
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	@Override
	public ImageBean getImage(String imageMD5){
		try{
			return dm.daoGetImage(imageMD5);
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	@Override
	public ImageBean getImage(String primaryKey,String refType){
		try{
			return dm.daoGetImage(primaryKey,
					RefSrcType.valueOf(MoreObjects.firstNonNull(refType,RefSrcType.DEFAULT.name())));
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	@Override
	public List<String> getImagesAssociatedByFeature(String featureMd5){
		try{
			return dm.daoGetImageKeysImportedByFeatureMd5(featureMd5);
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	
	@Override
	public FaceBean getFace(int faceId){
		try{
			return dm.daoGetFace(faceId);
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	@Override
	public List<FaceBean> getFacesOfFeature(String featureMd5){
		try{
			return dm.daoGetFacesOfFeature(featureMd5);
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	@Override
	public List<FaceBean> getFacesOfImage(String imageMd5){
		try{
			return dm.daoGetFacesOfImage(imageMd5);
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	@Override
	public Integer getDeviceIdOfFeature(String featureMd5) {
		try{
			return dm.daoGetDeviceIdOfFeature(featureMd5);
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	@Override
	public int deleteImage(final String imageMd5, Token token){
		try{
			Enable.ALL.check(tm, token);
			tm.checkPermissionForImage(token, imageMd5);
			return BaseDao.daoRunAsTransaction(new Callable<Integer>() {

				@Override
				public Integer call() throws Exception {
					return dm.daoDeleteImage(imageMd5);
				}
			});
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		}
	}

    @Override
	public boolean existsDevice(int id)  {
		try{
			return dm.daoExistsDevice(id);
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		} 
	}

	@Override
	public DeviceBean saveDevice(DeviceBean deviceBean, Token token){
		try{
			Enable.PERSON_ONLY.check(tm, token);
			tm.checkPermissionForDevice(token, deviceBean);
			return dm.daoSaveDevice(deviceBean);
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	@Override
	public DeviceBean updateDevice(DeviceBean deviceBean, Token token){
		try{
			Enable.ALL.check(tm, token);
			tm.checkPermissionForDevice(token, deviceBean);
			checkArgument(null != deviceBean && !deviceBean.isNew(),
					"require the device must be exists record");
			dm.daoSaveDevice(deviceBean);
			// 重新获取设备记录，以确保返回值是更新后的完整记录
			return dm.daoGetDeviceChecked(deviceBean.getId());
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	@Override
	public DeviceBean getDevice(int deviceId){
		try{
			return dm.daoGetDevice(deviceId);
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		} 
	}
	@Override
	public DeviceBean getDeviceByMac(String mac){
		try{
			return dm.daoGetDeviceByIndexMac(mac);
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		} 
	}
	@Override
	public List<DeviceBean> getDevices(List<Integer> idList){
		try{
			return dm.daoGetDevices(idList);
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		} 
	}
	@Override
	public List<DeviceBean> loadDeviceByWhere(String where,int startRow, int numRows){
		try{
			return this.dm.daoLoadDeviceByWhere(where, startRow, numRows);
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	@Override
	public List<DeviceBean> loadDeviceByWhere(String where,int startRow, int numRows,Token token){
		try{
			Enable.ALL.check(tm, token);
			return this.dm.daoLoadDeviceByWhere(tm.normailizeWhereForDevice(token,where), startRow, numRows);
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	@Override
	public int countDeviceByWhere(String where){
		try{
			return this.dm.daoCountDeviceByWhere(where);
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	@Override
	public int countDeviceByWhere(String where,Token token){
		try{
			Enable.ALL.check(tm, token);
			return this.dm.daoCountDeviceByWhere(tm.normailizeWhereForDevice(token, where));
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	@Override
	public List<Integer> loadDeviceIdByWhere(String where){
		try{
			return this.dm.daoLoadDeviceIdByWhere(where);
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	@Override
	public List<Integer> loadDeviceIdByWhere(String where,Token token){
		try{
			Enable.ALL.check(tm, token);
			return this.dm.daoLoadDeviceIdByWhere(tm.normailizeWhereForDevice(token,where));
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	////////////////////////////////DeviceGroupBean/////////////
	
	@Override
	public DeviceGroupBean saveDeviceGroup(DeviceGroupBean deviceGroupBean, Token token) {
		try{
			Enable.PERSON_ONLY.check(tm, token);
			tm.checkPermissionForDeviceGroup(token, deviceGroupBean);
			return dm.daoSaveDeviceGroup(deviceGroupBean);
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	@Override
	public DeviceGroupBean getDeviceGroup(int deviceGroupId) {
		try{
			return dm.daoGetDeviceGroup(deviceGroupId);
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	@Override
	public List<DeviceGroupBean> getDeviceGroups(List<Integer> groupIdList) {
		try{
			return dm.daoGetDeviceGroups(groupIdList); 
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	@Override
	public int deleteDeviceGroup(int deviceGroupId, Token token) {
		try{
			Enable.PERSON_ONLY.check(tm, token);
			tm.checkPermissionForDeviceGroup(token, deviceGroupId);
			return dm.daoDeleteDeviceGroup(deviceGroupId);
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	@Override
	public List<Integer> getSubDeviceGroup(int deviceGroupId) {
		try{
			return dm.daoToPrimaryKeyListFromDeviceGroups(dm.daoGetSubDeviceGroup(deviceGroupId));
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	@Override
	public List<Integer> getDevicesOfGroup(int deviceGroupId) {
		try{
			return dm.daoToPrimaryKeyListFromDevices(dm.daoGetDevicesOfGroup(deviceGroupId));
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	@Override
	public List<Integer> listOfParentForDeviceGroup(int deviceGroupId){
		try{
			return dm.daoToPrimaryKeyListFromDeviceGroups(dm.daoListOfParentForDeviceGroup(deviceGroupId));
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	@Override
	public List<Integer> childListForDeviceGroup(int deviceGroupId){
		try{
			return dm.daoToPrimaryKeyListFromDeviceGroups(dm.daoChildListByParentForDeviceGroup(deviceGroupId));
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	@Override
	public List<Integer> getDeviceGroupsBelongs(int deviceId)throws ServiceRuntimeException{
		try{
			DeviceBean deviceBean = dm.daoGetDevice(deviceId);
			return null == deviceBean 
						? ImmutableList.<Integer>of()
						: listOfParentForDeviceGroup(deviceBean.getGroupId());
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		}
	}

	////////////////////////////////PersonGroupBean/////////////
	
	@Override
	public PersonGroupBean savePersonGroup(PersonGroupBean personGroupBean, Token token) {
		try{
			Enable.PERSON_ONLY.check(tm, token);
			tm.checkPermissionForPersonGroup(token, personGroupBean);
			return dm.daoSavePersonGroup(personGroupBean);
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	@Override
	public PersonGroupBean getPersonGroup(int personGroupId) {
		try{
			return dm.daoGetPersonGroup(personGroupId); 
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	@Override
	public List<PersonGroupBean> getPersonGroups(List<Integer> groupIdList) {
		try{
			return dm.daoGetPersonGroups(groupIdList);
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	@Override
	public int deletePersonGroup(int personGroupId, Token token) {
		try{
			Enable.PERSON_ONLY.check(tm, token);
			tm.checkPermissionForPersonGroup(token, personGroupId);
			return dm.daoDeletePersonGroup(personGroupId);
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	@Override
	public List<Integer> getSubPersonGroup(int personGroupId) {
		try{
			return dm.daoToPrimaryKeyListFromPersonGroups(dm.daoGetSubPersonGroup(personGroupId));
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	@Override
	public List<Integer> getPersonsOfGroup(int personGroupId) {
		try{
			return this.dm.daoToPrimaryKeyListFromPersons(dm.daoGetPersonsOfGroup(personGroupId));
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	@Override
	public List<Integer> listOfParentForPersonGroup(int personGroupId){
		try{
			return dm.daoToPrimaryKeyListFromPersonGroups(dm.daoListOfParentForPersonGroup(personGroupId));
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	@Override
	public List<Integer> childListForPersonGroup(int personGroupId){
		try{
			return dm.daoToPrimaryKeyListFromPersonGroups(dm.daoChildListByParentForPersonGroup(personGroupId));
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	@Override
	public List<Integer> getPersonGroupsBelongs(int personId){
		try{
			PersonBean personBean = dm.daoGetPerson(personId);
			return null == personBean 
						? ImmutableList.<Integer>of()
						: listOfParentForPersonGroup(personBean.getGroupId());
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		}
	}
    @Override
    public List<Integer> loadDeviceGroupByWhere(String where,int startRow, int numRows){
		try{
			return dm.daoToPrimaryKeyListFromDeviceGroups(dm.daoLoadDeviceGroupByWhere(where, startRow, numRows));
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		}
    }

    @Override
    public int countDeviceGroupByWhere(String where){
		try{
			return dm.daoCountDeviceGroupByWhere(where);
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		}
    }
    @Override
	public int countDeviceGroupByWhere(String where,Token token){
    	try{
    		Enable.ALL.check(tm, token);
    		return dm.daoCountDeviceGroupByWhere(tm.normailizeWhereForDeviceGroup(token, where));
    	} catch (Exception e) {
    		throw wrapServiceRuntimeException(e);
    	}
    }
    @Override
    public List<Integer> loadDeviceGroupIdByWhere(String where){
    	try{
    		return dm.daoLoadDeviceGroupIdByWhere(where);
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		}
    }
    @Override
	public List<Integer> loadDeviceGroupIdByWhere(String where,Token token){
    	try{
    		Enable.ALL.check(tm, token);
    		return	dm.daoLoadDeviceGroupIdByWhere(tm.normailizeWhereForDeviceGroup(token,where));
    	} catch (Exception e) {
    		throw wrapServiceRuntimeException(e);
    	}
    }
	/////////////////////MANAGEMENT BORDER/////

	@Override
	public void bindBorder(final int personGroupId,final int deviceGroupId, Token token) {
    	try{
			Enable.ROOT_ONLY.check(tm, token);
			BaseDao.daoRunAsTransaction(new Runnable(){
				@Override
				public void run() {
					dm.daoBindBorder(personGroupId,deviceGroupId);
				}});	    		
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		}
	}

	@Override
	public void unbindBorder(int personGroupId,int deviceGroupId, Token token) {
    	try{
    		Enable.ROOT_ONLY.check(tm, token);
    		dm.daoUnbindBorder(personGroupId,deviceGroupId);
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		}
	}

	@Override
	public Integer rootGroupOfPerson(int personId){
    	try{
    		return dm.daoRootGroupOfPerson(personId);
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	@Override
	public Integer rootGroupOfPersonGroup(int groupId){
    	try{
    		return dm.daoRootGroupOfPersonGroup(groupId);
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		}
	}

	@Override
	public Integer rootGroupOfDevice(int deviceId){
    	try{
    		return dm.daoRootGroupOfDevice(deviceId);
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	@Override
	public Integer rootGroupOfDeviceGroup(int groupId){
    	try{
    		return dm.daoRootGroupOfDeviceGroup(groupId);
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	/////////////////////PERMIT/////
	@Override
	public PermitBean savePermit(PermitBean permitBean, Token token){
		try{
			Enable.PERSON_ONLY.check(tm, token);
			tm.checkPermissionForPermit(token, permitBean);
			return dm.daoSavePermit(permitBean);
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	
	@Override
	public PermitBean savePermit(int deviceGroupId,int personGroupId, String column, String value, Token token){
		try{
			Enable.PERSON_ONLY.check(tm, token);
			tm.checkPermissionForDeviceGroup(token, deviceGroupId);
			tm.checkPermissionForPersonGroup(token, personGroupId);
			return dm.daoSavePermit(deviceGroupId, personGroupId,column, value);
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	@Override
	public int deletePermit(int deviceGroupId,int personGroupId, Token token) {
		try{
			Enable.PERSON_ONLY.check(tm, token);
			tm.checkPermissionForDeviceGroup(token, deviceGroupId);
			tm.checkPermissionForPersonGroup(token, personGroupId);
			return dm.daoDeletePermit(deviceGroupId, personGroupId);
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	@Override
	public int deletePersonGroupPermit(int personGroupId,Token token){
		try{
			Enable.PERSON_ONLY.check(tm, token);
			tm.checkPermissionForPersonGroup(token, personGroupId);
			return dm.daoDeletePersonGroupPermit(personGroupId);
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	@Override
	public int deleteGroupPermitOnDeviceGroup(int deviceGroupId, Token token){
		try{
			Enable.PERSON_ONLY.check(tm, token);
			tm.checkPermissionForDeviceGroup(token, deviceGroupId);
			return dm.daoDeleteGroupPermitOnDeviceGroup(deviceGroupId);
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	@Override
	public PermitBean getGroupPermitOnDeviceGroup(int deviceGroupId,int personGroupId) {
		try{
			return dm.daoGetGroupPermitOnDeviceGroup(deviceGroupId,personGroupId);
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	@Override
	public PermitBean getGroupPermit(int deviceId,int personGroupId) {
		try{
			return dm.daoGetGroupPermit(deviceId,personGroupId);
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	@Override
	public PermitBean getPersonPermit(int deviceId,int personId) {
		try{
			return dm.daoGetPersonPermit(deviceId,personId);
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	@Override
	public List<PermitBean> getGroupPermits(int deviceId,List<Integer> personGroupIdList) {
		try{
			return dm.daoGetGroupPermit(deviceId, personGroupIdList);
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	@Override
	public List<PermitBean> getPersonPermits(int deviceId,List<Integer> personIdList) {
		try{
			return dm.daoGetPermit(deviceId, personIdList);
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	@Override
	public List<Integer> getPersonGroupsPermittedBy(int deviceGroupId){
		try{
			return dm.daoGetPersonGroupsPermittedBy(deviceGroupId);
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	@Override
	public List<Integer> getDeviceGroupsPermittedBy(int personGroupId){
		try{
			return dm.daoGetDeviceGroupsPermittedBy(personGroupId);
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	@Override
	public List<Integer> getDeviceGroupsPermit(int personGroupId){
		try{
			return dm.daoGetDeviceGroupsPermit(personGroupId);
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		}
	}

	@Override
	public List<PermitBean> loadPermitByUpdate(long timestamp) {
		try{
			return dm.daoLoadPermitByCreateTime(null, new Date(timestamp));
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	@Override
	public List<PermitBean> loadPermitByUpdate(String timestamp) {
		try{
			return dm.daoLoadPermitByCreateTime(null,toDate(checkNotNull(timestamp,"timestamp is null")));
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		}
	}
    @Override
    public List<Integer> loadPersonGroupByWhere(String where,int startRow, int numRows){
    	try{
    		return dm.daoToPrimaryKeyListFromPersonGroups(dm.daoLoadPersonGroupByWhere(where, startRow, numRows));
    	} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		}
    }

    @Override
    public int countPersonGroupByWhere(String where){
    	try{
    		return dm.daoCountPersonGroupByWhere(where);
    	} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		}
    }
    @Override
	public int countPersonGroupByWhere(String where,Token token){
    	try{
    		Enable.ALL.check(tm, token);
    		return dm.daoCountPersonGroupByWhere(tm.normailizeWhereForPersonGroup(token, where));
    	} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		}
    }
    @Override
    public List<Integer> loadPersonGroupIdByWhere(String where){
    	try{
    		return dm.daoLoadPersonGroupIdByWhere(where);
    	} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		}
    }
    @Override
	public List<Integer> loadPersonGroupIdByWhere(String where,Token token){
    	try{
    		Enable.ALL.check(tm, token);
    		return dm.daoLoadPersonGroupIdByWhere(tm.normailizeWhereForPersonGroup(token, where));
    	} catch (Exception e) {
    		throw wrapServiceRuntimeException(e);
    	}
    }

	@Override
    public DeviceBean registerDevice(DeviceBean newDevice) throws ServiceSecurityException{
    	try{
    		TokenContext.getCurrentTokenContext().setIgnoreToken(true);
    		return tm.registerDevice(newDevice);
    	} catch (RuntimeException e) {
			return  throwServiceException(e);
		}
    }
    @Override
	public void unregisterDevice(Token token)
			throws ServiceSecurityException{
    	try{
    		Enable.DEVICE_ONLY.check(tm, token);
    		checkArgument(token != null, "token is null");
    		tm.unregisterDevice(token.getId());
    	} catch (RuntimeException e) {
			throwServiceException(e);
		}
	}
    @Override
	public boolean deleteDevice(int id,Token token){
		try{
			Enable.PERSON_ONLY.check(tm, token);
			tm.checkPermissionForDevice(token, id);
			// tm.checkRank(token, PersonRank.admin);
			return 1 == dm.daoDeleteDevice(id);
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		}	
	}
	@Override
	public boolean deleteDeviceByMac(String mac,Token token){
		try{
			Enable.PERSON_ONLY.check(tm, token);
			tm.checkPermissionForDevice(token, mac);
			// tm.checkRank(token, PersonRank.admin);
			return 1 == dm.daoDeleteDeviceByIndexMac(mac);  		
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		}	
	}
	@Override
	public Token online(DeviceBean device)
			throws ServiceSecurityException{
    	try{
    		return tm.applyDeviceToken(device);
    	} catch (RuntimeException e) {
			return throwServiceException(e);
		}
	}
    @Override
	public void offline(Token token)
			throws ServiceSecurityException{
    	try{
    		tm.releaseDeviceToken(token);
    	} catch (RuntimeException e) {
			throwServiceException(e);
		}
	}
    @Override
	public Token applyPersonToken(int personId, String password, boolean isMd5)
			throws ServiceSecurityException{
    	try{
    		return tm.applyPersonToken(personId, password, isMd5);
    	} catch (RuntimeException e) {
			return throwServiceException(e);
		}
	}
    @Override
	public void releasePersonToken(Token token)
			throws ServiceSecurityException{
    	try{
    		tm.releasePersonToken(token);
    	} catch (RuntimeException e) {
			throwServiceException(e);
		}
	}
    @Override
	public Token applyRootToken(String password, boolean isMd5)
			throws ServiceSecurityException{
		try{
			return tm.applyRootToken(password, isMd5);
		} catch (RuntimeException e) {
			return throwServiceException(e);
		}
	}
	@Override
	public void releaseRootToken(Token token)
			throws ServiceSecurityException{
    	try{
    		tm.releaseRootToken(token);
    	} catch (RuntimeException e) {
			throwServiceException(e);
		}
	}
	@Override
	public Token applyUserToken(int userid,String password,boolean isMd5) throws ServiceSecurityException{
		try{
			if(userid == -1){
				return tm.applyRootToken(password, isMd5);
			}else{
				return tm.applyPersonToken(userid, password, isMd5);
			}
		} catch (RuntimeException e) {
			return throwServiceException(e);
		}
	}
	@Override
	public void releaseUserToken(Token token)
			throws ServiceSecurityException{
    	try{
    		if(token != null){
    			switch (token.getType()) {
				case PERSON:
					tm.releasePersonToken(token);
					break;
				case ROOT:
					tm.releaseRootToken(token);
					break;
				default:
					throw new ServiceSecurityException("UNSUPPORTED TOKEN TYPE " + token.getType());
				}	
    		}
    	} catch (RuntimeException e) {
			throwServiceException(e);
		}
	}
	@Override
	public boolean isValidPassword(String userId,String password, boolean isMd5) {
    	try{
    		return tm.isValidPassword(userId, password, isMd5);
    	} catch (ServiceSecurityException e) {
			return false;
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		}
	}
    @Override
    public String applyAckChannel(Token token) {
    	return applyAckChannel(0,token);
	}
    @Override
    public String applyAckChannel(int duration, Token token) {
    	try {
    		Enable.PERSON_ONLY.check(tm, token);
			return tm.applyAckChannel(token.getId(), duration);
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		} 
	}
    @Override
    public int applyCmdSn(Token token) {
    	try {
    		Enable.PERSON_ONLY.check(tm, token);
			return tm.applyCmdSn(token.getId());
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		} 
	}
    @Override
    public boolean isValidCmdSn(int cmdSn) {
    	try {
			return tm.isValidCmdSn(cmdSn);
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		} 
	}
    @Override
    public boolean isValidAckChannel(String ackChannel) {
    	try {
			return tm.isValidAckChannel(ackChannel);
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		} 
	}
	@Override
	public boolean isValidDeviceToken(Token token){
    	try {
			return tm.isValidDeviceToken(token);
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		} 
	}
	@Override
	public boolean isValidPersonToken(Token token){
    	try {
			return tm.isValidPersonToken(token);
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		} 
	}
	@Override
	public boolean isValidRootToken(Token token){
    	try {
			return tm.isValidRootToken(token);
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		} 
	}
	
	@Override
	public boolean isValidUserToken(Token token){
    	try {
   			return tm.isValidUserToken(token);
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		} 
	}
	@Override
	public boolean isValidToken(Token token){
    	try {
   			return tm.isValidToken(token);
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		} 
	}
    @Override
    public Map<MQParam,String> getRedisParameters(Token token){
    	try {
			Enable.ALL.check(tm, token);
			return rm.getRedisParameters();
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		} 
    }
    @Override
	public Map<MQParam,String> getMessageQueueParameters(Token token){
    	try {
			Enable.ALL.check(tm, token);
			return mm.getMessageQueueParameters();
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		} 
    }
    @Override
	public Map<String,String> getFaceApiParameters(Token token){
    	try {
			Enable.ALL.check(tm, token);
			return fm.getConfig();
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		} 
    }
	@Override
	public String taskQueueOf(String task,Token token) {	
    	try {
			Enable.ALL.check(tm, token);
			return rm.taskQueueOf(task);
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		} 
	}
	@Override
	public String sdkTaskQueueOf(String task,String sdkVersion,Token token) {	
    	try {
			Enable.ALL.check(tm, token);
			return rm.sdkTaskQueueOf(task,sdkVersion);
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		} 
	}
	
	@Override
	public String runCmd(List<Integer>target,boolean group,String cmdpath,String jsonArgs,String ackChannel,Token token){
		try {
			Enable.PERSON_ONLY.check(tm, token);
			if(group){
				tm.checkPermissionForDeviceGroup(token, target);
			}else{
				tm.checkPermissionForDevice(token, target);
			}
			checkArgument(tm.isUserToken(token),"type of token must be PERSON or ROOT");			
			return dc.doRunCmd(target, group, cmdpath, jsonArgs, ackChannel, token.getId()); 
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		} 
	}
	@Override
	public Integer runTask(String taskQueue,String cmdpath,String jsonArgs,String ackChannel,Token token){
		try {
			Enable.PERSON_ONLY.check(tm, token);
			checkArgument(tm.isUserToken(token),"type of token must be PERSON or ROOT");
			return dc.doRunTask(taskQueue, cmdpath, jsonArgs, ackChannel, token.getId()); 
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		} 
	}
	@Override
	public String runTaskSync(String taskQueue,String cmdpath,String jsonArgs,int timeoutSecs,Token token){
		try {
			Enable.PERSON_ONLY.check(tm, token);
			checkArgument(tm.isUserToken(token),"type of token must be PERSON or ROOT");
			return BaseJsonEncoder.getEncoder().toJsonString(dc.doRunTaskSync(taskQueue, cmdpath, jsonArgs, timeoutSecs,token.getId())); 
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		} 
	}
	@Override
	public List<String> loadDistinctStringColumn(String table,String column,String where){
		try{
			Iterable<?> iterable = Iterables.filter(dm.daoLoadColumnAsList(table, column, true, where),Predicates.notNull());
			return Lists.newArrayList(Iterables.transform(iterable, Functions.toStringFunction()));
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		} 
	}
	@SuppressWarnings("unchecked")
	@Override
	public List<Integer> loadDistinctIntegerColumn(String table,String column,String where){
		try{
			RowMetaData metadata = checkNotNull(RowMetaData.getMetaData(table),"INVALID table name %s",table);
			int columnId = metadata.columnIDOf(column);
			checkArgument(columnId >=0,"INVALID column %s",column);
			checkArgument(Integer.class.equals(metadata.columnTypeOf(columnId)),
					"java type of %s.%s column is not Integer",table,column);
			Iterable<?> iterable = Iterables.filter(dm.daoLoadColumnAsList(table, column, true, where),Predicates.notNull());
			return Lists.newArrayList((Iterable<Integer>)iterable);
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		} 
	}

    @Override
    public String getProperty(String key,Token token){
    	try {
			Enable.ALL.check(tm, token);			
			if(!TokenType.ROOT.equals(token.getType())){
				PropertyWhiteList.INSTANCE.checkAccess(key);
			}
			return GlobalConfig.getProperty(key);
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		} 	
    }
    @Override
	public Map<String, String> getProperties(String prefix,Token token){
    	try {
			Enable.ALL.check(tm, token);
			if(!TokenType.ROOT.equals(token.getType())){
				PropertyWhiteList.INSTANCE.checkAccess(prefix);
			}
			return GlobalConfig.getProperties(prefix);
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		} 	
    }
    @Override
    public Map<String,String> getServiceConfig(Token token){
    	try {
			Enable.ROOT_ONLY.check(tm, token);
			return GlobalConfig.getProperties("");
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		} 	
    }
    @Override
    public void setProperty(String key,String value,Token token){
    	try {
			Enable.ROOT_ONLY.check(tm, token);
			GlobalConfig.setProperty(key,value);
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		} 	
    }
    @Override
    public void setProperties(Map<String,String> config,Token token){
    	try {
			Enable.ROOT_ONLY.check(tm, token);
			GlobalConfig.setProperties(config);
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		} 	
    }
    @Override
    public void saveServiceConfig(Token token){
    	try {
			Enable.ROOT_ONLY.check(tm, token);
			GlobalConfig.persistence();
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		}
    }
    @Override
	public String iso8601Time(){
    	SimpleDateFormat dataFormat = new SimpleDateFormat(ISO8601_FORMATTER_STR);
    	return dataFormat.format(new Date());
    }
    @Override
	public String createTempPwd(int targetId,TmpPwdTargetType targetType,String expiryDate,Token token){
    	try {
			Enable.PERSON_ONLY.check(tm, token);
			tm.checkPermissionForTmpPwd(token,targetId,targetType);
			return tp.createTempPwd(targetId, targetType, toDate(expiryDate));
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		}
    }
    @Override
	public String createTempPwd(int targetId,TmpPwdTargetType targetType,int duration,Token token){
    	try {
			Enable.PERSON_ONLY.check(tm, token);
			tm.checkPermissionForTmpPwd(token,targetId,targetType);
			return tp.createTempPwd(targetId, targetType, TempPwdManagement.getExpiryDate(duration));
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		}
    }
    @Override
	public TmpwdTargetInfo getTargetInfo4PwdOnDevice(String pwd,Token token){
    	try {
			Enable.DEVICE_ONLY.check(tm, token);
			return tp.getTargetInfo4PwdOnDevice(pwd,token.getId());
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		}
    }
	@Override
	public int faceRecognizePersonPermitted(byte[] imageData,Float threshold, int group, int deviceId, boolean searchInPermited){
		try{
			return fm.permit(imageData, threshold, group, deviceId, searchInPermited, null);
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	
	@Override
	public LockWakeupResponse lockWakeup(DeviceBean deviceBean,boolean ignoreSchedule,String sdkVersion) throws ServiceSecurityException{
    	try{
    		TokenContext.getCurrentTokenContext().setIgnoreToken(true);
    		LockWakeupResponse response = new LockWakeupResponse();
    		SimpleDateFormat dataFormat = new SimpleDateFormat(ISO8601_FORMATTER_STR);
    		response.setIso8601Timestamp(dataFormat.format(new Date()));
    		response.setDeviceBean( tm.registerDevice(deviceBean));
    		response.setToken(tm.applyDeviceToken(response.getDeviceBean()));
    		response.setTmpPasswords(tp.getAllPasswordsOnDevice(response.getDeviceBean().getId()));
    		Set<PersonBean> persons = dm.daoGetPersonsPermittedOnDevice(response.getDeviceBean().getId(), ignoreSchedule,  null, null);
    		List<PersonSummary> infoList = Lists.newLinkedList();
    		for(PersonBean person : persons){
    			List<String> list = dm.daoToPrimaryKeyListFromFeatures(dm.daoGetFeaturesByPersonIdAndSdkVersion(person.getId(),sdkVersion));
    			if(!list.isEmpty()){
    				Date expiryDate = person.getExpiryDate();
    				String s = null == expiryDate ? null : dataFormat.format(expiryDate);
    				infoList.add(new PersonSummary(person.getId(),list, s, null));
    			}
    		}
    		response.setPersons(infoList);
    		return response;
    	} catch (RuntimeException e) {
    		throw wrapServiceRuntimeException(e);
		}
	}
	
	@Override
	public int initTopGroup(final TopGroupInfo groupInfo,Token token){
		try {
			Enable.ROOT_ONLY.check(tm, token);
			return BaseDao.daoRunAsTransaction(new Callable<Integer>(){
				@Override
				public Integer call() throws Exception {
					return dm.initTopGroup(groupInfo);
				}});	
		} catch (Exception e) {
    		throw wrapServiceRuntimeException(e);
		}
	}
	@Override
	public List<Integer>getGroupIdsByPath(String tablename,String path){
		try{
			return dm.daoGetGroupIdsByPath(tablename,path);
		}catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	@Override
	public String pathOf(String tablename,int groupId){
		try{
			return dm.daoPathOf(tablename,groupId);
		}catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	@Override
	public List<MatchEntry> fuzzySearch(String tablename, 
			String column, 
			String pattern, 
			StringMatchType matchType, 
			int matchFlags, 
			int parentGroupId, 
			int maxMatchCount) 
			throws FuzzyMatchCountExceedLimitException{
		try{
			// 不允许匿名搜索人员表
			checkArgument(!"fl_person".equals(tablename),"UNSUPPORT fuzzy person searh for anonymous caller");
			return fmm.fuzzySearch(
					checkNotNull(tablename,"tablename is null"),
					column, 
					checkNotNull(pattern,"pattern is null"), 
					matchType, 
					matchFlags, 
					parentGroupId, 
					maxMatchCount);
		} catch (RuntimeException e) {
			throw wrapServiceRuntimeException(e);
		}
	}
	
	@Override
	public List<MatchEntry> fuzzySearchPerson(String column, 
			String pattern, 
			StringMatchType matchType, 
			int matchFlags,
			int parentGroupId, 
			int maxMatchCount, 
			Token token) 
			throws FuzzyMatchCountExceedLimitException{
		try{
			Enable.PERSON_ONLY.check(tm, token);
			tm.checkPermissionForPersonGroup(token, parentGroupId);
			return fmm.fuzzySearch(
					"fl_person",
					checkNotNull(column,"column is null"), 
					checkNotNull(pattern,"pattern is null"), 
					matchType, 
					matchFlags, 
					parentGroupId,
					maxMatchCount);
		} catch (Exception e) {
			throw wrapServiceRuntimeException(e);
		}
	}
    @Override
	public String version(){
		return Version.VERSION;
	}
    @Override
    public Map<String, String> versionInfo(){
		return Version.INFO;
	}
	@Override
	public boolean isLocal() {
		return true;
	}
}
